Using R in hydrology (EGU2017 short course)

These slides and all other course materials can be found at:

github.com/brry/rhydro #slides

To get all the material including the datasets and presentation source code, we recommend to download the whole github course repository.

This is an R Markdown Notebook.
For discussions, please visit the Hydrology in R Facebook group.
Before running the code blocks below, we suggest to get package installation instructions by running:

source("https://raw.githubusercontent.com/brry/rhydro/master/checkpc.R")


Aim and contents of this workshop

We want to:

We can not:

We have prepared:

 

Before we get started, please let us know your current R knowledge level by filling out the short survey at
bit.ly/knowR


top

Report

Good coding practice, report generation (Rstudio, rmarkdown, R notebook)
Daniel Klotz

Introduction

goals

goals

Why I use R

Why I did not use:

equals

equals

Whats great about R:

  library(ggplot2)
  test_data <- mpg
  test_plot <- ggplot(test_data, aes(displ, hwy, colour = class)) + 
    geom_point()
  test_plot

Why I decided to use R:

pipe

pipe

Previously:

  aggregation_function <- function(x) {
    round(mean(x),2)
  }
  mtcars_subset <- subset(mtcars,hp > 100)
  mtcars_aggregated <- aggregate(. ~ cyl, data = mtcars_subset, FUN = aggregation_function)
  car_data1 <- transform(mtcars_aggregated, kpl = mpg*0.4251)
  print(car_data1)

Now:

library(magrittr)
car_data2 <- 
  mtcars %>%
  subset(hp > 100) %>%
  aggregate(. ~ cyl, data = ., FUN = . %>% mean %>% round(2)) %>%
  transform(kpl = mpg %>% multiply_by(0.4251)) %>%
  print

btw: You can integrate other programming languages with ease. Here an example from Yihui Xie for the use of Fortran in Rmarkdown:

  1. Compile Code:
```r
C Fortran test
      subroutine fexp(n, x)
      double precision x
C  output
      integer n, i
C  input value
      do 10 i=1,n
         x=dexp(dcos(dsin(dble(float(i)))))
  10  continue
      return
      end
```
  1. Run Code:
```r
res = .Fortran("fexp", n=100000L, x=0)
str(res)
```

Be happy with the result: > ## List of 2 > ## $ n: int 100000 > ## $ x: num 2.72


Markdown

HTML: HyperText Markdown Language

John Gruber:

John Gruber

Comparison: Markdown vs. Latex Comparison

Rstudio provides cheat-sheets with the most important informations about many of their “favorite” packages & software:

Cheat Sheet

Rmarkdown

In Rstudio:

Rmark1

Rmark2

Rmark2

“Native” Formats:

Much more possible if you adress pandoc directly: pandoc

Information in the text can be automatically updated with the rest of the document: [time for coffee

Examples

Small Websites

Cayman Theme

Cayman Theme

Books (blogdown)

bookdown1 bookdown2

Blogs (hugo)

hugo

Widgets

cran-gauge superzip

Presentations

bookdown1

Apps (Shiny)

shiny shiny2


top

GIS

Using R as GIS (reading a rainfall shapefile + Kriging, sf + leaflet + mapview + OSMscale)
Berry Boessenkool

Shapefiles

Reading shapefiles with maptools::readShapeSpatial and rgdal::readOGR is obsolete.
Instead, use sf::st_read. sf is on CRAN since oct 2016.
Main reaction when using sf: “Wow, that is fast!”
Download the shapefile or better: download the whole github course repository

rain <- sf::st_read("data/PrecBrandenburg/niederschlag.shp")
centroids <- sf::st_centroid(rain)
centroids <- sf::st_coordinates(centroids)

top

Plotting, maps

Static plot:

plot(rain[,1])

Static map:

prj <- sf::st_crs(rain)$proj4string
centroids <- as.data.frame(centroids)
cent_ll <- OSMscale::projectPoints(Y,X, data=centroids, to=OSMscale::pll(), from=prj)
map_static <- OSMscale::pointsMap(y,x, cent_ll, fx=0.08, type="maptoolkit-topo", zoom=6)

Interactive map:

library(leaflet)
cent_ll$info <- paste0(sample(letters,nrow(cent_ll),TRUE), ", ", round(cent_ll$x,2), 
                       ", ", round(cent_ll$y,2))
leaflet(cent_ll) %>% addTiles() %>% addCircleMarkers(lng=~x, lat=~y, popup=~info)

Interactive map of shapefile:

# devtools::install_github("environmentalinformatics-marburg/mapview", ref = "develop")
library(berryFunctions) # classify, seqPal
col <- seqPal(n=100, colors=c("red","yellow","blue"))[classify(rain$P1)$index]
mapview::mapview(rain, col.regions=col)

top

Kriging

Plot original points colored by third dimension:

pcol <- colorRampPalette(c("red","yellow","blue"))(50)
x <- centroids$X # use cent_ll$x for projected data
y <- centroids$Y
berryFunctions::colPoints(x, y, rain$P1, add=FALSE, col=pcol)

Calculate the variogram and fit a semivariance curve

library(geoR)
geoprec <- as.geodata(cbind(x,y,rain$P1))
vario <- variog(geoprec, max.dist=130000) # other maxdist for lat-lon data
fit <- variofit(vario)
plot(vario)
lines(fit)

Determine a useful resolution (keep in mind that computing time rises exponentially with grid size)

# distance to closest other point:
d <- sapply(1:length(x), function(i)
            min(berryFunctions::distance(x[i], y[i], x[-i], y[-i])) )
# for lat-long data use (2017-Apr only available in github version of OSMscale)
# d <- OSMscale::maxEarthDist(y,x, data=cent_ll, fun=min)
hist(d/1000, breaks=20, main="distance to closest gauge [km]")
mean(d/1000) # 8 km

Perform kriging on a grid with that resolution

res <- 1000 # 1 km, since stations are 8 km apart on average
grid <- expand.grid(seq(min(x),max(x),res),
                    seq(min(y),max(y),res))
krico <- krige.control(type.krige="OK", obj.model=fit)
#krobj <- krige.conv(geoprec, loc=grid, krige=krico)
#save(krobj, file="data/krobj.Rdata")
load("data/krobj.Rdata") # line above is too slow for recreation each time

Plot the interpolated values with or an equivalent (see Rclick 4.15) and add contour lines.

par(mar=c(0,3,0,3))
geoR:::image.kriging(krobj, col=pcol)
colPoints(x, y, rain$P1, col=pcol, legargs=list(horiz=F, title="Prec",y1=0.1,x1=0.9))
points(x,y)
plot(rain, col=NA, add=TRUE)


top

Discharge

River discharge time-series visualisation and extreme value statistics (animation + extremeStat)
Berry Boessenkool


top

Hydmod

Hydrological modelling with airGR
Katie Smith


top

EDA

Exploratory Data Analysis including flow duration curve and trend analysis on time-series
Shaun Harrigan


top

Discussion

Please give us feedback at bit.ly/feedbackR

For discussions, please use the Hydrology in R Facebook group.

LS0tDQp0aXRsZTogIlJoeWRybyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IG5vbmUNCiAgICB0b2M6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBGQUxTRSkNCmBgYA0KDQojIFVzaW5nIFIgaW4gaHlkcm9sb2d5IChFR1UyMDE3IHNob3J0IGNvdXJzZSkNCg0KVGhlc2Ugc2xpZGVzIGFuZCBhbGwgb3RoZXIgY291cnNlIG1hdGVyaWFscyBjYW4gYmUgZm91bmQgYXQ6DQoNCjxmb250IHNpemU9IjYiPltnaXRodWIuY29tL2Jycnkvcmh5ZHJvXShodHRwczovL2dpdGh1Yi5jb20vYnJyeS9yaHlkcm8pPC9mb250PiANCjxmb250IHNpemU9IjQiPiNzbGlkZXM8L2ZvbnQ+ICAgDQoNClRvIGdldCBhbGwgdGhlIG1hdGVyaWFsIGluY2x1ZGluZyB0aGUgZGF0YXNldHMgYW5kIHByZXNlbnRhdGlvbiBzb3VyY2UgY29kZSwgd2UgcmVjb21tZW5kIHRvDQpbZG93bmxvYWQgdGhlIHdob2xlIGdpdGh1YiBjb3Vyc2UgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL2Jycnkvcmh5ZHJvL2FyY2hpdmUvbWFzdGVyLnppcCkuDQoNClRoaXMgaXMgYW4gW1IgTWFya2Rvd24gTm90ZWJvb2tdKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vcl9ub3RlYm9va3MuaHRtbCkuICANCkZvciBkaXNjdXNzaW9ucywgcGxlYXNlIHZpc2l0IHRoZSANCltIeWRyb2xvZ3kgaW4gUiBGYWNlYm9vayBncm91cF0oaHR0cHM6Ly93d3cuZmFjZWJvb2suY29tL2dyb3Vwcy8xMTMwMjE0Nzc3MTIzOTA5LykuICANCkJlZm9yZSBydW5uaW5nIHRoZSBjb2RlIGJsb2NrcyBiZWxvdywgd2Ugc3VnZ2VzdCB0byBnZXQgcGFja2FnZSBpbnN0YWxsYXRpb24gaW5zdHJ1Y3Rpb25zIGJ5IHJ1bm5pbmc6DQpgYGBSDQpzb3VyY2UoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9icnJ5L3JoeWRyby9tYXN0ZXIvY2hlY2twYy5SIikNCmBgYA0KDQpcDQoNCioqQWltIGFuZCBjb250ZW50cyBvZiB0aGlzIHdvcmtzaG9wKioNCg0KV2Ugd2FudCB0bzogIA0KDQoqIFNob3cgb2ZmIGhvdyBhd2Vzb21lIFIgaXMgZm9yIGh5ZHJvbG9neSAoaXQncyBSLXNvbWUhXl4pICANCiogQ29udmluY2UgeW91IHRvIHN0YXJ0IG9yIGNvbnRpbnVlIHVzaW5nIFIgIA0KKiBQcm92aWRlIGFsbCB0aGUgY29kZSBmb3IgeW91IGFzIGEgc3RhcnRpbmcgcG9pbnQNCg0KV2UgY2FuIG5vdDogIA0KDQoqIFRlYWNoIHlvdSBhY3R1YWwgUiBjb2RpbmcgKDkwIG1pbnMgaXMgdG9vIHNob3J0IGZvciBhIHR1dG9yaWFsKQ0KDQpXZSBoYXZlIHByZXBhcmVkOg0KDQoqIFtHb29kIGNvZGluZyBwcmFjdGljZSwgcmVwb3J0IGdlbmVyYXRpb25dKCNyZXBvcnQpIChSc3R1ZGlvLCBgcm1hcmtkb3duYCwgUiBub3RlYm9vaykNCiogW1VzaW5nIFIgYXMgR0lTXSgjZ2lzKSAocmVhZGluZyBhIHJhaW5mYWxsIHNoYXBlZmlsZSArIEtyaWdpbmcsIGBzZmAgKyBgbGVhZmxldGAgKyBgbWFwdmlld2AgKyBgT1NNc2NhbGVgKQ0KKiBbUml2ZXIgZGlzY2hhcmdlIHRpbWUtc2VyaWVzXSgjZGlzY2hhcmdlKSB2aXN1YWxpc2F0aW9uIGFuZCBleHRyZW1lIHZhbHVlIHN0YXRpc3RpY3MgKGBhbmltYXRpb25gICsgYGV4dHJlbWVTdGF0YCkNCiogW0h5ZHJvbG9naWNhbCBtb2RlbGxpbmddKCNoeWRtb2QpIHdpdGggYGFpckdSYA0KKiBbRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpc10oI2VkYSkgaW5jbHVkaW5nIGZsb3cgZHVyYXRpb24gY3VydmUgYW5kIHRyZW5kIGFuYWx5c2lzIG9uIHRpbWUtc2VyaWVzDQoNClwgDQoNCkJlZm9yZSB3ZSBnZXQgc3RhcnRlZCwgcGxlYXNlIGxldCB1cyBrbm93IHlvdXIgY3VycmVudCBSIGtub3dsZWRnZSBsZXZlbCBieSBmaWxsaW5nIG91dCB0aGUgc2hvcnQgc3VydmV5IGF0ICANCjxmb250IHNpemU9IjYiPltiaXQubHkva25vd1JdKGh0dHBzOi8vYml0Lmx5L2tub3dSKTwvZm9udD4gDQoNClwNCg0KW3RvcF0oI3RvcCkNCg0KIyBSZXBvcnQNCkdvb2QgY29kaW5nIHByYWN0aWNlLCBbcmVwb3J0IGdlbmVyYXRpb25dKCNyZXBvcnQpIChSc3R1ZGlvLCBgcm1hcmtkb3duYCwgUiBub3RlYm9vaykgIA0KKipEYW5pZWwgS2xvdHoqKg0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KIVtnb2Fsc10oZGFuaWVsL2ludHJvL2dvYWxzLmpwZWcpDQoNCiMjIyBXaHkgSSB1c2UgUg0KDQpXaHkgSSBkaWQgbm90IHVzZTogDQoNCiFbZXF1YWxzXShkYW5pZWwvaW50cm8vZXF1YWxzLmpwZWcpDQoNCg0KV2hhdHMgZ3JlYXQgYWJvdXQgUjogDQpgYGB7cn0NCiAgbGlicmFyeShnZ3Bsb3QyKQ0KICB0ZXN0X2RhdGEgPC0gbXBnDQogIHRlc3RfcGxvdCA8LSBnZ3Bsb3QodGVzdF9kYXRhLCBhZXMoZGlzcGwsIGh3eSwgY29sb3VyID0gY2xhc3MpKSArIA0KICAgIGdlb21fcG9pbnQoKQ0KICB0ZXN0X3Bsb3QNCmBgYA0KDQpXaHkgSSBkZWNpZGVkIHRvIHVzZSBSOiANCg0KIVtwaXBlXShkYW5pZWwvaW50cm8vbWFncml0dHIuanBlZykNCg0KUHJldmlvdXNseToNCmBgYHtyfQ0KICBhZ2dyZWdhdGlvbl9mdW5jdGlvbiA8LSBmdW5jdGlvbih4KSB7DQogICAgcm91bmQobWVhbih4KSwyKQ0KICB9DQogIG10Y2Fyc19zdWJzZXQgPC0gc3Vic2V0KG10Y2FycyxocCA+IDEwMCkNCiAgbXRjYXJzX2FnZ3JlZ2F0ZWQgPC0gYWdncmVnYXRlKC4gfiBjeWwsIGRhdGEgPSBtdGNhcnNfc3Vic2V0LCBGVU4gPSBhZ2dyZWdhdGlvbl9mdW5jdGlvbikNCiAgY2FyX2RhdGExIDwtIHRyYW5zZm9ybShtdGNhcnNfYWdncmVnYXRlZCwga3BsID0gbXBnKjAuNDI1MSkNCiAgcHJpbnQoY2FyX2RhdGExKQ0KYGBgDQoNCk5vdzoNCmBgYHtyfQ0KbGlicmFyeShtYWdyaXR0cikNCmNhcl9kYXRhMiA8LSANCiAgbXRjYXJzICU+JQ0KICBzdWJzZXQoaHAgPiAxMDApICU+JQ0KICBhZ2dyZWdhdGUoLiB+IGN5bCwgZGF0YSA9IC4sIEZVTiA9IC4gJT4lIG1lYW4gJT4lIHJvdW5kKDIpKSAlPiUNCiAgdHJhbnNmb3JtKGtwbCA9IG1wZyAlPiUgbXVsdGlwbHlfYnkoMC40MjUxKSkgJT4lDQogIHByaW50DQpgYGANCg0KDQoqKmJ0dzoqKg0KWW91IGNhbiBpbnRlZ3JhdGUgb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIHdpdGggZWFzZS4gSGVyZSBhbiBleGFtcGxlIGZyb20gDQpbWWlodWkgWGllXShodHRwczovL3lpaHVpLm5hbWUva25pdHIvZGVtby9lbmdpbmVzLykgZm9yIHRoZSB1c2Ugb2YgYEZvcnRyYW5gIA0KaW4gUm1hcmtkb3duOg0KDQoxLiBDb21waWxlIENvZGU6DQogICAgYGBge3IgY29tcGZvcnQsIGVuZ2luZT0nZm9ydHJhbicsIHJlc3VsdHM9J2hpZGUnLCBldmFsPUZBTFNFfQ0KICAgIEMgRm9ydHJhbiB0ZXN0DQogICAgICAgICAgc3Vicm91dGluZSBmZXhwKG4sIHgpDQogICAgICAgICAgZG91YmxlIHByZWNpc2lvbiB4DQogICAgQyAgb3V0cHV0DQogICAgICAgICAgaW50ZWdlciBuLCBpDQogICAgQyAgaW5wdXQgdmFsdWUNCiAgICAgICAgICBkbyAxMCBpPTEsbg0KICAgICAgICAgICAgIHg9ZGV4cChkY29zKGRzaW4oZGJsZShmbG9hdChpKSkpKSkNCiAgICAgIDEwICBjb250aW51ZQ0KICAgICAgICAgIHJldHVybg0KICAgICAgICAgIGVuZA0KICAgIGBgYA0KDQoyLiBSdW4gQ29kZToNCiAgICBgYGB7ciB0ZXN0Zm9ydCwgY29sbGFwc2U9VFJVRSwgZXZhbCA9IEZBTFNFfQ0KICAgIHJlcyA9IC5Gb3J0cmFuKCJmZXhwIiwgbj0xMDAwMDBMLCB4PTApDQogICAgc3RyKHJlcykNCiAgICBgYGANCg0KQmUgaGFwcHkgd2l0aCB0aGUgcmVzdWx0OiANCj4gICAgIGAjIyBMaXN0IG9mIDJgDQo+ICAgICBgIyMgICQgbjogaW50IDEwMDAwMGANCj4gICAgIGAjIyAgJCB4OiBudW0gMi43MmANCg0KLS0tDQoNCiMjIE1hcmtkb3duIA0KSFQqKk0qKkw6IEh5cGVyVGV4dCAqKk1hcmtkb3duKiogTGFuZ3VhZ2UNCg0KSm9obiBHcnViZXI6DQoNClshW0pvaG4gR3J1YmVyXShkYW5pZWwvaW50cm8vSm9obl9HcnViZXJfd2lraS5qcGVnKV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSm9obl9HcnViZXIpDQoNCg0KQ29tcGFyaXNvbjogTWFya2Rvd24gdnMuIExhdGV4IA0KWyFbQ29tcGFyaXNvbl0oZGFuaWVsL2ludHJvL3lpaHVpX2xhdGV4LXZzLW1hcmtkb3duLnBuZyldKGh0dHBzOi8veW91dHUuYmUvMnl2VzBPXzd4T2cpDQoNCg0KUnN0dWRpbyBwcm92aWRlcyBjaGVhdC1zaGVldHMgd2l0aCB0aGUgbW9zdCBpbXBvcnRhbnQgaW5mb3JtYXRpb25zIGFib3V0IG1hbnkgDQpvZiB0aGVpciAiZmF2b3JpdGUiIHBhY2thZ2VzICYgc29mdHdhcmU6DQoNClshW0NoZWF0IFNoZWV0XShkYW5pZWwvaW50cm8vUm1hcmtkb3duX2NoZWF0c2hlZXQucG5nKV0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykNCg0KIyBSbWFya2Rvd24NCkluIFJzdHVkaW86IA0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KICA8aW1nIHdpZHRoPSI2MDBweCIgc3JjPSJkYW5pZWwvcmVwb3J0cy9SbWFyazEucG5nIiBhbHQ9IlJtYXJrMSIgLz4NCjwvZGl2Pg0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KICA8aW1nIHdpZHRoPSI2MDBweCIgc3JjPSJkYW5pZWwvcmVwb3J0cy9SbWFyazIucG5nIiBhbHQ9IlJtYXJrMiIgLz4NCjwvZGl2Pg0KDQo8ZGl2IGFsaWduPSJjZW50ZXIiPg0KICA8aW1nIHdpZHRoPSI2MDBweCIgc3JjPSJkYW5pZWwvcmVwb3J0cy9SbWFyazMucG5nIiBhbHQ9IlJtYXJrMiIgLz4NCjwvZGl2Pg0KDQoiTmF0aXZlIiBGb3JtYXRzOg0KDQotIFtodG1sXShkYW5pZWwvc2hvdy9SbWFyay5odG1sKSANCi0gW3BkZl0oZGFuaWVsL3Nob3cvUm1hcmsucGRmKSANCi0gW3dvcmRdKGRhbmllbC9zaG93L1JtYXJrLmRvY3MpDQoNCk11Y2ggbW9yZSBwb3NzaWJsZSBpZiB5b3UgYWRyZXNzIHBhbmRvYyBkaXJlY3RseTogDQpbIVtwYW5kb2NdKGRhbmllbC9yZXBvcnRzL3BhbmRvY19kaWFncmFtLmpwZyldKGh0dHA6Ly9wYW5kb2Mub3JnLykNCg0KSW5mb3JtYXRpb24gaW4gdGhlIHRleHQgY2FuIGJlIGF1dG9tYXRpY2FsbHkgdXBkYXRlZCB3aXRoIHRoZSByZXN0IG9mIHRoZSANCmRvY3VtZW50Og0KWyFbdGltZSBmb3IgY29mZmVlXShkYW5pZWwvcmVwb3J0cy9jb2ZmZWUucG5nKQ0KDQojIyMgRXhhbXBsZXMNCg0KIyMjIyBTbWFsbCBXZWJzaXRlcw0KPGRpdiBhbGlnbj0iY2VudGVyIj4NCiAgPGEgaHJlZiA9ICJodHRwOi8vcnN0dWRpby5naXRodWIuaW8vdHVmdGUvIj4NCiAgICA8aW1nIHdpZHRoPSI2MDBweCIgc3JjPSJkYW5pZWwvZXhhbXBsZXMvc3dfdHVmdGUucG5nIiBhbHQ9IkNheW1hbiBUaGVtZSIgLz4NCiAgPC9hPg0KPC9kaXY+DQoNCjxkaXYgYWxpZ249ImNlbnRlciI+DQogIDxhIGhyZWYgPSAiaHR0cDovL3lpeHVhbi5jb3MubmFtZS9wcmV0dHlkb2MvY2F5bWFuLmh0bWwiPg0KICAgIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9leGFtcGxlcy9zd19jYXltYW4ucG5nIiBhbHQ9IkNheW1hbiBUaGVtZSIgLz4NCiAgPC9hPg0KPC9kaXY+DQoNCiMjIyMgQm9va3MgKGJsb2dkb3duKQ0KWyFbYm9va2Rvd24xXShkYW5pZWwvZXhhbXBsZXMvYm9va2Rvd25fMS5wbmcpXShodHRwczovL2Jvb2tkb3duLm9yZy8pDQpbIVtib29rZG93bjJdKGRhbmllbC9leGFtcGxlcy9ib29rZG93bl8yLnBuZyldKGh0dHBzOi8vYm9va2Rvd24ub3JnLykNCg0KIyMjIEJsb2dzIChodWdvKQ0KWyFbaHVnb10oZGFuaWVsL2V4YW1wbGVzL2Jsb2dzX2h1Z28xLnBuZyldKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL2Jsb2dkb3duLykNCg0KIyMjIyBXaWRnZXRzIA0KWyFbY3Jhbi1nYXVnZV0oZGFuaWVsL2V4YW1wbGVzL3dpZGdldHNfMS5wbmcpXShodHRwczovL2dhbGxlcnkuc2hpbnlhcHBzLmlvL2NyYW4tZ2F1Z2UvKQ0KWyFbc3VwZXJ6aXBdKGRhbmllbC9leGFtcGxlcy93aWRnZXRzXzIucG5nKV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5L3N1cGVyemlwLWV4YW1wbGUuaHRtbCkNCg0KIyMjIyBQcmVzZW50YXRpb25zDQpbIVtib29rZG93bjFdKGRhbmllbC9leGFtcGxlcy9wcmVzZW50YXRpb25zXzEucG5nKV0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9yZXZlYWxqc19wcmVzZW50YXRpb25fZm9ybWF0Lmh0bWwpDQoNCiMjIyMgQXBwcyAoU2hpbnkpIA0KWyFbc2hpbnldKGRhbmllbC9leGFtcGxlcy9zaGlueV8xLnBuZyldKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vKQ0KWyFbc2hpbnkyXShkYW5pZWwvZXhhbXBsZXMvc2hpbnlfMl9nYWxsZXJ5LnBuZyldKGh0dHBzOi8vc2hpbnkucnN0dWRpby5jb20vZ2FsbGVyeS9zdXBlcnppcC1leGFtcGxlLmh0bWwpDQoNCg0KXA0KW3RvcF0oI3RvcCkNCg0KIyBHSVMNClVzaW5nIFIgYXMgR0lTIChyZWFkaW5nIGEgcmFpbmZhbGwgc2hhcGVmaWxlICsgS3JpZ2luZywgYHNmYCArIGBsZWFmbGV0YCArIGBtYXB2aWV3YCArIGBPU01zY2FsZWApICANCioqQmVycnkgQm9lc3Nlbmtvb2wqKg0KDQojIyMgU2hhcGVmaWxlcw0KDQpSZWFkaW5nIHNoYXBlZmlsZXMgd2l0aCBgbWFwdG9vbHM6OnJlYWRTaGFwZVNwYXRpYWxgIGFuZCBgcmdkYWw6OnJlYWRPR1JgIGlzIG9ic29sZXRlLiAgDQpJbnN0ZWFkLCB1c2UgYHNmOjpzdF9yZWFkYC4gYHNmYCBpcyBvbiBDUkFOIHNpbmNlIG9jdCAyMDE2LiAgDQpNYWluIHJlYWN0aW9uIHdoZW4gdXNpbmcgc2Y6ICJXb3csIHRoYXQgaXMgZmFzdCEiICANCltEb3dubG9hZCB0aGUgc2hhcGVmaWxlXShodHRwczovL21pbmhhc2thbWFsLmdpdGh1Yi5pby9Eb3duR2l0LyMvaG9tZT91cmw9aHR0cHM6Ly9naXRodWIuY29tL2Jycnkvcmh5ZHJvL3RyZWUvbWFzdGVyL3ByZXNlbnRhdGlvbnMvZGF0YS9QcmVjQnJhbmRlbmJ1cmcpIA0Kb3IgYmV0dGVyOiBbZG93bmxvYWQgdGhlIHdob2xlIGdpdGh1YiBjb3Vyc2UgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL2Jycnkvcmh5ZHJvL2FyY2hpdmUvbWFzdGVyLnppcCkNCg0KYGBge3J9DQpyYWluIDwtIHNmOjpzdF9yZWFkKCJkYXRhL1ByZWNCcmFuZGVuYnVyZy9uaWVkZXJzY2hsYWcuc2hwIikNCmNlbnRyb2lkcyA8LSBzZjo6c3RfY2VudHJvaWQocmFpbikNCmNlbnRyb2lkcyA8LSBzZjo6c3RfY29vcmRpbmF0ZXMoY2VudHJvaWRzKQ0KYGBgDQoNClt0b3BdKCN0b3ApDQoNCiMjIyBQbG90dGluZywgbWFwcw0KDQpTdGF0aWMgcGxvdDoNCmBgYHtyfQ0KcGxvdChyYWluWywxXSkNCmBgYA0KDQpTdGF0aWMgbWFwOg0KYGBge3J9DQpwcmogPC0gc2Y6OnN0X2NycyhyYWluKSRwcm9qNHN0cmluZw0KY2VudHJvaWRzIDwtIGFzLmRhdGEuZnJhbWUoY2VudHJvaWRzKQ0KY2VudF9sbCA8LSBPU01zY2FsZTo6cHJvamVjdFBvaW50cyhZLFgsIGRhdGE9Y2VudHJvaWRzLCB0bz1PU01zY2FsZTo6cGxsKCksIGZyb209cHJqKQ0KbWFwX3N0YXRpYyA8LSBPU01zY2FsZTo6cG9pbnRzTWFwKHkseCwgY2VudF9sbCwgZng9MC4wOCwgdHlwZT0ibWFwdG9vbGtpdC10b3BvIiwgem9vbT02KQ0KYGBgDQoNCkludGVyYWN0aXZlIG1hcDoNCmBgYHtyfQ0KbGlicmFyeShsZWFmbGV0KQ0KY2VudF9sbCRpbmZvIDwtIHBhc3RlMChzYW1wbGUobGV0dGVycyxucm93KGNlbnRfbGwpLFRSVUUpLCAiLCAiLCByb3VuZChjZW50X2xsJHgsMiksIA0KICAgICAgICAgICAgICAgICAgICAgICAiLCAiLCByb3VuZChjZW50X2xsJHksMikpDQpsZWFmbGV0KGNlbnRfbGwpICU+JSBhZGRUaWxlcygpICU+JSBhZGRDaXJjbGVNYXJrZXJzKGxuZz1+eCwgbGF0PX55LCBwb3B1cD1+aW5mbykNCmBgYA0KDQpJbnRlcmFjdGl2ZSBtYXAgb2Ygc2hhcGVmaWxlOg0KYGBge3J9DQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZW52aXJvbm1lbnRhbGluZm9ybWF0aWNzLW1hcmJ1cmcvbWFwdmlldyIsIHJlZiA9ICJkZXZlbG9wIikNCmxpYnJhcnkoYmVycnlGdW5jdGlvbnMpICMgY2xhc3NpZnksIHNlcVBhbA0KY29sIDwtIHNlcVBhbChuPTEwMCwgY29sb3JzPWMoInJlZCIsInllbGxvdyIsImJsdWUiKSlbY2xhc3NpZnkocmFpbiRQMSkkaW5kZXhdDQptYXB2aWV3OjptYXB2aWV3KHJhaW4sIGNvbC5yZWdpb25zPWNvbCkNCmBgYA0KDQpbdG9wXSgjdG9wKQ0KDQojIyMgS3JpZ2luZw0KDQpQbG90IG9yaWdpbmFsIHBvaW50cyBjb2xvcmVkIGJ5IHRoaXJkIGRpbWVuc2lvbjoNCmBgYHtyfQ0KcGNvbCA8LSBjb2xvclJhbXBQYWxldHRlKGMoInJlZCIsInllbGxvdyIsImJsdWUiKSkoNTApDQp4IDwtIGNlbnRyb2lkcyRYICMgdXNlIGNlbnRfbGwkeCBmb3IgcHJvamVjdGVkIGRhdGENCnkgPC0gY2VudHJvaWRzJFkNCmJlcnJ5RnVuY3Rpb25zOjpjb2xQb2ludHMoeCwgeSwgcmFpbiRQMSwgYWRkPUZBTFNFLCBjb2w9cGNvbCkNCmBgYA0KDQpDYWxjdWxhdGUgdGhlIHZhcmlvZ3JhbSBhbmQgZml0IGEgc2VtaXZhcmlhbmNlIGN1cnZlDQpgYGB7cn0NCmxpYnJhcnkoZ2VvUikNCmdlb3ByZWMgPC0gYXMuZ2VvZGF0YShjYmluZCh4LHkscmFpbiRQMSkpDQp2YXJpbyA8LSB2YXJpb2coZ2VvcHJlYywgbWF4LmRpc3Q9MTMwMDAwKSAjIG90aGVyIG1heGRpc3QgZm9yIGxhdC1sb24gZGF0YQ0KZml0IDwtIHZhcmlvZml0KHZhcmlvKQ0KcGxvdCh2YXJpbykNCmxpbmVzKGZpdCkNCmBgYA0KDQpEZXRlcm1pbmUgYSB1c2VmdWwgcmVzb2x1dGlvbiANCihrZWVwIGluIG1pbmQgdGhhdCBjb21wdXRpbmcgdGltZSByaXNlcyBleHBvbmVudGlhbGx5IHdpdGggZ3JpZCBzaXplKQ0KYGBge3J9DQojIGRpc3RhbmNlIHRvIGNsb3Nlc3Qgb3RoZXIgcG9pbnQ6DQpkIDwtIHNhcHBseSgxOmxlbmd0aCh4KSwgZnVuY3Rpb24oaSkNCiAgICAgICAgICAgIG1pbihiZXJyeUZ1bmN0aW9uczo6ZGlzdGFuY2UoeFtpXSwgeVtpXSwgeFstaV0sIHlbLWldKSkgKQ0KIyBmb3IgbGF0LWxvbmcgZGF0YSB1c2UgKDIwMTctQXByIG9ubHkgYXZhaWxhYmxlIGluIGdpdGh1YiB2ZXJzaW9uIG9mIE9TTXNjYWxlKQ0KIyBkIDwtIE9TTXNjYWxlOjptYXhFYXJ0aERpc3QoeSx4LCBkYXRhPWNlbnRfbGwsIGZ1bj1taW4pDQpoaXN0KGQvMTAwMCwgYnJlYWtzPTIwLCBtYWluPSJkaXN0YW5jZSB0byBjbG9zZXN0IGdhdWdlIFtrbV0iKQ0KbWVhbihkLzEwMDApICMgOCBrbQ0KYGBgDQoNClBlcmZvcm0ga3JpZ2luZyBvbiBhIGdyaWQgd2l0aCB0aGF0IHJlc29sdXRpb24gDQpgYGB7cn0NCnJlcyA8LSAxMDAwICMgMSBrbSwgc2luY2Ugc3RhdGlvbnMgYXJlIDgga20gYXBhcnQgb24gYXZlcmFnZQ0KZ3JpZCA8LSBleHBhbmQuZ3JpZChzZXEobWluKHgpLG1heCh4KSxyZXMpLA0KICAgICAgICAgICAgICAgICAgICBzZXEobWluKHkpLG1heCh5KSxyZXMpKQ0Ka3JpY28gPC0ga3JpZ2UuY29udHJvbCh0eXBlLmtyaWdlPSJPSyIsIG9iai5tb2RlbD1maXQpDQoja3JvYmogPC0ga3JpZ2UuY29udihnZW9wcmVjLCBsb2M9Z3JpZCwga3JpZ2U9a3JpY28pDQojc2F2ZShrcm9iaiwgZmlsZT0iZGF0YS9rcm9iai5SZGF0YSIpDQpsb2FkKCJkYXRhL2tyb2JqLlJkYXRhIikgIyBsaW5lIGFib3ZlIGlzIHRvbyBzbG93IGZvciByZWNyZWF0aW9uIGVhY2ggdGltZQ0KYGBgDQoNClBsb3QgdGhlIGludGVycG9sYXRlZCB2YWx1ZXMgd2l0aCBccmNvZGV7aW1hZ2V9IG9yIGFuIGVxdWl2YWxlbnQgDQooc2VlIFtSY2xpY2tdKGh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JjbGljaykgNC4xNSkgYW5kIGFkZCBjb250b3VyIGxpbmVzLg0KYGBge3J9DQpwYXIobWFyPWMoMCwzLDAsMykpDQpnZW9SOjo6aW1hZ2Uua3JpZ2luZyhrcm9iaiwgY29sPXBjb2wpDQpjb2xQb2ludHMoeCwgeSwgcmFpbiRQMSwgY29sPXBjb2wsIGxlZ2FyZ3M9bGlzdChob3Jpej1GLCB0aXRsZT0iUHJlYyIseTE9MC4xLHgxPTAuOSkpDQpwb2ludHMoeCx5KQ0KcGxvdChyYWluLCBjb2w9TkEsIGFkZD1UUlVFKQ0KYGBgDQoNClwNClt0b3BdKCN0b3ApDQoNCiMgRGlzY2hhcmdlDQpSaXZlciBkaXNjaGFyZ2UgdGltZS1zZXJpZXMgdmlzdWFsaXNhdGlvbiBhbmQgZXh0cmVtZSB2YWx1ZSBzdGF0aXN0aWNzIChgYW5pbWF0aW9uYCArIGBleHRyZW1lU3RhdGApICANCioqQmVycnkgQm9lc3Nlbmtvb2wqKg0KDQpcDQpbdG9wXSgjdG9wKQ0KDQojIEh5ZG1vZA0KSHlkcm9sb2dpY2FsIG1vZGVsbGluZyB3aXRoIGBhaXJHUmAgICANCioqS2F0aWUgU21pdGgqKg0KDQpcDQpbdG9wXSgjdG9wKQ0KDQojIEVEQQ0KRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyBpbmNsdWRpbmcgZmxvdyBkdXJhdGlvbiBjdXJ2ZSBhbmQgdHJlbmQgYW5hbHlzaXMgb24gdGltZS1zZXJpZXMgICANCioqU2hhdW4gSGFycmlnYW4qKg0KDQpcDQpbdG9wXSgjdG9wKQ0KDQoNCiMgRGlzY3Vzc2lvbg0KDQpQbGVhc2UgZ2l2ZSB1cyBmZWVkYmFjayBhdA0KPGZvbnQgc2l6ZT0iNiI+W2JpdC5seS9mZWVkYmFja1JdKGh0dHBzOi8vYml0Lmx5L2ZlZWRiYWNrUik8L2ZvbnQ+IA0KDQpGb3IgZGlzY3Vzc2lvbnMsIHBsZWFzZSB1c2UgdGhlIA0KW0h5ZHJvbG9neSBpbiBSIEZhY2Vib29rIGdyb3VwXShodHRwczovL3d3dy5mYWNlYm9vay5jb20vZ3JvdXBzLzExMzAyMTQ3NzcxMjM5MDkvKS4gIA0KDQo=